package io.github.cchenxi.crpc.core.consumer;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

import io.github.cchenxi.crpc.core.annotation.RpcConsumer;
import io.github.cchenxi.crpc.core.api.Loadbalancer;
import io.github.cchenxi.crpc.core.api.RegistryCenter;
import io.github.cchenxi.crpc.core.api.Router;
import io.github.cchenxi.crpc.core.api.RpcContext;
import io.github.cchenxi.crpc.core.meta.InstanceMeta;
import io.github.cchenxi.crpc.core.meta.ServiceMeta;
import io.github.cchenxi.crpc.core.utils.MethodUtil;
import lombok.Data;

/**
 * 服务消费者启动器
 * Date: 2024-03-10
 *
 * @author chenxi
 */
@Data
public class ConsumerBootstrap implements ApplicationContextAware, EnvironmentAware {
    ApplicationContext applicationContext;
    Environment environment;

    @Value("${app.id}")
    private String app;

    @Value("${app.namespace}")
    private String namespace;

    @Value("${app.env}")
    private String env;

    private Map<String, Object> stub = new HashMap<>();

    public void start() {
        Router<InstanceMeta> router = applicationContext.getBean(Router.class);
        Loadbalancer<InstanceMeta> loadbalancer = applicationContext.getBean(Loadbalancer.class);
        RegistryCenter registryCenter = applicationContext.getBean(RegistryCenter.class);

        RpcContext context = new RpcContext();
        context.setRouter(router);
        context.setLoadBalancer(loadbalancer);

        String[] names = applicationContext.getBeanDefinitionNames();
        for (String name : names) {
            Object bean = applicationContext.getBean(name);

            if (!name.contains("crpcSimpleExampleConsumerApplication")) {
                continue;
            }

            List<Field> fields = MethodUtil.findAnnotatedField(bean.getClass(), RpcConsumer.class);

            fields.stream().forEach(field -> {
                System.out.println(field.getName());
                Class<?> service = field.getType();
                String serviceName = service.getCanonicalName();
                Object consumer = stub.get(serviceName);
                if (Objects.isNull(consumer)) {
                    consumer = createFromRegistry(service, context, registryCenter);
                }
                stub.putIfAbsent(serviceName, consumer);
                field.setAccessible(true);
                try {
                    field.set(bean, consumer);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    private Object createFromRegistry(Class<?> service, RpcContext context, RegistryCenter registryCenter) {
        ServiceMeta serviceMeta = ServiceMeta.builder().app(app).namespace(namespace).env(env)
                .name(service.getCanonicalName()).build();
        List<InstanceMeta> providers = registryCenter.fetchAll(serviceMeta);
        System.out.println("===> map to providers");
        providers.forEach(System.out::println);

        // 订阅
        registryCenter.subscribe(serviceMeta, event -> {
            providers.clear();
            providers.addAll(event.getData());
        });

        return createConsumer(service, providers, context);
    }

    private Object createConsumer(Class<?> service, List<InstanceMeta> providers, RpcContext context) {
        return Proxy.newProxyInstance(service.getClassLoader(),
                new Class[]{service}, new RpcInvocationHandler(service, providers, context));
    }
}
